#
# This file is part of netclique, a social network app for google appengine.
#
# http://code.google.com/p/netclique/
# 
# Netclique is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# Netclique is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import ast, re, urlparse, urllib


REGEX = re.compile(r'\{{2}[^{](.*?)[^\}]\}{2}', re.DOTALL)


class Template(ast.NodeTransformer):
    """ Class to render text/python-code
        
        Example:
            My values:
            {{ for i in range(3): }}
                value: "{{ i }}" 
                -> {{ if not i: }} first {{ else: }} next {{ endif }}!
            {{ endfor }}
            
        Result:
            My values:

                value: "0" 
                ->  first !

                value: "1" 
                ->  next !

                value: "2" 
                ->  next !
                
        Usage:
            t = Template(string)
            t = Template(string, a=1, b=2)
            t = Template.load(path/to/file)
            t = Template.load(path/to/file, a=1, b=2)
            
            t()
            t(a=1, b=2)
            print t
    """

    # class variable holds loaded templates
    paths = {}
    
    # convenient flag prohibit template reload
    cache = True

   #_ = None
    
    def __init__(self, data, **kwargs):        
        offset = 0
        token = []
        
        for i, part in enumerate(REGEX.split(data)):
            part = part.replace('\\{', '{').replace('\\}', '}')
                
            if not part: 
                continue
            
            if i % 2:
                if part.startswith('end'):
                    offset -= 1
                    continue

                if part.startswith(('else', 'elif')):
                    offset -= 1
                    
                lines = part.splitlines()
                margin = min(len(l)-len(l.lstrip()) for l in lines if l.strip())
                part = '\n'.join('\t' * offset + l[margin:] for l in lines)
            
                if part.endswith(':'):
                    offset += 1
                    
            else:
                part = part.replace('\\', '\\\\').replace('"', '\\"')
                part = '\t' * offset + '"""%s"""' % part

            token.append(part)
        
        self.code = compile(self.visit(ast.parse('\n'.join(token))), '', 'exec')
        self.kwargs = kwargs
        
    def __call__(self, **kwargs): 
        kw = self.kwargs
        kw.update(kwargs)
        output = []
        
        def _print(*args, **kwargs):
            sep = kwargs.get('sep', '')
            end = kwargs.get('end', '\n')
            output.append(sep.join(map(str, args))+end)
 
        eval(self.code, kw,  {'print': _print})

        return ''.join(map(str, output))

    __str__ = __call__
        
    def visit_Expr(self, node):
        # if the node we are operating on has child nodes we must
        # call the generic_visit() method for the node first:
        ast.NodeTransformer.generic_visit(self, node)

        return ast.copy_location(
            ast.Expr(
                value=ast.Call(
                    func=ast.Name(
                        id='print', 
                        ctx=ast.Load(),
                        lineno=node.lineno,
                        col_offset=node.col_offset
                    ),
                    args=hasattr(node, 'value') and [node.value] or [],
                    keywords=[
                        ast.keyword(
                            arg='sep', 
                            value=ast.Str(
                                s='', 
                                lineno=node.lineno, 
                                col_offset=node.col_offset
                            )
                        ), 
                        ast.keyword(
                            arg='end', 
                            value=ast.Str(
                                s='', 
                                lineno=node.lineno, 
                                col_offset=node.col_offset
                            )
                       )
                    ],
                    lineno=node.lineno,
                    col_offset=node.col_offset
                )
            ),
            node
        )
        
    @classmethod    
    def load(cls, path, **kwargs):
        if not cls.cache or path not in cls.paths:
            f = open(path)
            try:
                data = f.read()
            except:
                data = path
            finally:
                f.close()
            cls.paths[path] = cls(data, **kwargs)
           
        return cls.paths[path]
        
        
class SecureTemplate(Template):

    def visit_Import(self, node):
        return None
        
    def visit_Call(self, node):
        try:
            if node.func.id in ['_', 'gettext']:
                return Template.generic_visit(self, node)
        except: 
            pass
        return None
        
    def generic_visit(self, node):
        return Template.generic_visit(self, node)
        
        
class Url(object):

    def __init__(self, path, *args, **kwargs):
        (   self.scheme, 
            self.netloc, 
            self.path, 
            self.params, 
            self.query,
            self.fragment
        ) = urlparse.urlparse(
            urlparse.urljoin(
                path, '/'.join(v.strip('/') for v in args)
            )
        )
        
        self.query = urlparse.parse_qs(self.query)
        self.query.update(kwargs)
        
    def __str__(self):
        return urlparse.urlunparse(
            (   self.scheme,
                self.netloc, 
                self.path, 
                self.params, 
                urllib.urlencode(self.query, True), 
                self.fragment
        ))
            
    @classmethod
    def url(cls, path, *args, **kwargs):
        return str(cls(path, *args, **kwargs))

        
        
